home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d1 / interce.arc / INTERCEP.C next >
C/C++ Source or Header  |  1989-04-18  |  11KB  |  419 lines

  1. /* intercept.c -- spawn a process and intercept BIOS and DOS
  2.  * SWI (software interrupt) calls, recording register data to
  3.  * a file.
  4.  * Demonstrates chaining interrupt handlers.
  5.  *************************************************************
  6.  * Solely for compilation under Borland's Turbo C.
  7.  *************************************************************
  8.  * Written 7/31/1987 by:
  9.  *     Ned Konz
  10.  *     210 Oleeta St.
  11.  *     Ormond Bch, FL 32074 (904)672-2431
  12.  *     BIX:nkonz  CIS:76046,223
  13.  *
  14.  * Released into the public domain by the author.
  15.  *
  16.  * Modified 4/11/1989 by:
  17.  *    Russell Nelson
  18.  *    11 Grant St.
  19.  *    Potsdam, NY 13676 (215)265-5655
  20.  *    CIS: 70441,205 GEnie: BH01 Internet: nelson@clutx.clarkson.edu
  21.  *    BITNET: nelson@clutx  UUCP: uunet!clutx.clarkson.edu!nelson
  22.  *
  23.  * No copyright is claimed by Russell Nelson
  24.  *
  25.  **************************************************************/
  26.  
  27. /*************************************************************
  28.  *    This program was written so I could find out quickly what
  29.  *    an unknown program's interface with the outside world was.
  30.  *    It does not currently handle hardware interrupts.
  31.  *    What it does:
  32.  *
  33.  *    1. Runs the specified program, intercepting certain interrupts
  34.  *        and recording data in memory.
  35.  *    2. Writes an intermediate file filled with Swi_info structures (binary)
  36.  *    3. Runs a program called "INTERPRE.EXE" to interpret the
  37.  *        intermediate file (It's a separate program to keep
  38.  *        this one small and so you can write your own.)
  39.  *        It's called like this:
  40.  *            interpre.exe [-l] infilename outfilename progname [args...]
  41.  *            where -l denotes long output format.
  42.  **************************************************************/
  43.  
  44. #include <dos.h>
  45. #include <stdio.h>
  46. #include <mem.h>
  47. #include <alloc.h>
  48. #include <process.h>
  49. #include <io.h>
  50. #include <string.h>
  51. #include <dir.h>
  52. #include "intercept.h"
  53.  
  54. char *usage =
  55. "[-l] [-T tmpdir] [-s maxcalls] [-o outfile] [-d datafile]\n"
  56. "\tprogram [args...]\n"
  57. "    -l    sets long format output: explanation AND register values\n"
  58. "    -T    sets temporary directory for intermediate file to \"tmpdir\"\n"
  59. "        (will use TMP or TMPDIR environment vars. if found otherwise)\n"
  60. "     -s    sets the maximum number of SWI records to \"maxcalls\"\n"
  61. "    -o    names the output filename to \"outfile\" rather than\n"
  62. "        the default name (\"intercep.out\")\n"
  63. "    -d    sets the interrupt data file to use, defaults to INTERPRE.DAT\n"
  64. "    program        is the name of the program to monitor\n"
  65. "    args        are any command-line arguments to be passed\n"
  66. "            to the monitored program.\n";
  67.  
  68. char *logo = "INTERCEPT -- monitor DOS and BIOS calls.    By:\n"
  69.     "    Ned Konz\n"
  70.     "    210 Oleeta St.\n"
  71.     "    Ormond Bch, FL 32074\n"
  72.     "    BIX:nkonz  CIS:76046,223  (904)672-2431\n"
  73.     "    08/02/1987\n"
  74.     "    Datafile option and parsing added by Russell Nelson\n";
  75.  
  76. char switchar,    /* DOS parameter switch char (from int 0x21, fn 0x3700) */
  77.     sepchar = '\\';        /* DOS filename separator */
  78.  
  79. void interrupt inthandler ( Regpack r, Intpack i );
  80. int get_swi_list(unsigned);
  81. void install(void);
  82. void uninstall(void);
  83.  
  84. /* the 8086/8088 INT instruction */
  85. #define SWI_INSTRUCTION    0xCD
  86.  
  87. /* int_table[] --
  88.  * table containg numbers of interrupts we're catching
  89.  * and their old handlers
  90.  * should be sorted by most common interrupts first.
  91.  * NO HARDWARE INTERRUPTS!!!
  92.  * Note: some of these may be commented out because they tend to
  93.  * quickly fill up the output file. Uncomment and re-compile
  94.  * if you want them too.
  95.  */
  96. Intblock
  97. int_table[MAX_INTS + 1];        /* leave room for a terminator */
  98.  
  99. /* swi_list[] --
  100.  * area in memory into which we store data about each SWI
  101.  */
  102. Swi_info huge *swi_list = NULL;    /* beginning of swi_list */
  103. Swi_info huge *swi_list_end = NULL;    /* just past end of swi_list */
  104. volatile Swi_info huge *swi_next = NULL;    /* pointer to next block */
  105.  
  106. /* our single interrupt handler
  107.  * which merely records our registers and interrupt number
  108.  * in swi_list[]
  109.  * and chains to old handler.
  110.  */
  111. void interrupt
  112. inthandler ( Regpack r, Intpack i )
  113. {
  114.     /* the following variables are declared as static to get them
  115.      * off the caller's stack and to ensure that t[] is where
  116.      * we want it to be: from [BP-01] through [BP-06]
  117.      */
  118.     static unsigned char far * caller;
  119.     static unsigned char which_int;
  120.     static IFP oldhandler;
  121.     static Intblock *ibp;
  122.     volatile unsigned t[3];    /* to move stack values down by 3 words into */
  123.  
  124.     /* point to next instruction */
  125.     caller = (char far *)i.ipcs;
  126.  
  127.     if (caller[-2] != SWI_INSTRUCTION) {    /* was this a non-SWI? (uh-oh!) */
  128.         uninstall();
  129.         exit(-1);
  130.     }
  131.  
  132.     which_int = caller[-1];    /* which SWI is this? */
  133.  
  134.     if (FP_SEG(i.ipcs) > _CS && FP_SEG(i.ipcs) < 0xA000
  135.         && swi_next < swi_list_end) {
  136.         swi_next->regs = r;
  137.         swi_next->caller = i;
  138.         swi_next->intnum = which_int;
  139.         swi_next++;
  140.     }
  141.  
  142.     /* get old handler value */
  143.     for (ibp=int_table; ibp->intnum>=0 && ibp->intnum!=which_int; ibp++)
  144.         ;
  145.  
  146.     if (ibp->intnum < 0)        /* can't happen... */
  147.         return;            /* but if it does, just return "safely" */
  148.  
  149.     oldhandler = ibp->oldint;
  150.  
  151.     /* move all our registers down by 3 words on the stack */
  152.     movedata(_SS, FP_OFF(&r), _SS, FP_OFF(t), sizeof(Regpack));
  153.  
  154.     /* supply a mock flag value with interrupts masked OFF */
  155.     r.ovl.new.flags = i.flags & ~0x0200;
  156.  
  157.     /* get the address of the routine to chain to */
  158.     r.ovl.new.ipcs = oldhandler;
  159.  
  160.     /* bump our frame pointer value down by 3 words --
  161.      * the stack pointer will be loaded from this new value next.
  162.      */
  163.     _BP -= 6;
  164.  
  165.     /* unstack all registers and do an IRET */
  166.     return;
  167. }
  168.  
  169. int
  170. get_swi_list(unsigned n)
  171. {
  172.     if (!(swi_list = farcalloc(n, sizeof(Swi_info))))
  173.         return 0;
  174.  
  175.     swi_next = swi_list;
  176.     swi_list_end = swi_list + n;
  177.     return n;
  178. }
  179.  
  180.  
  181. /* install our handler for all the named interrupts
  182.  */
  183. void
  184. install()
  185. {
  186.     Intblock *ibp;
  187.     IFPP vp;
  188.  
  189.     for (ibp = int_table; ibp->intnum >= 0; ibp++) {
  190.         vp = (IFPP) MK_FP(0, ibp->intnum*4);
  191.         ibp->oldint = *vp;
  192.         disable();
  193.         *vp = inthandler;
  194.         enable();
  195.     }
  196. }
  197.  
  198. /* un-install our handler for all the named interrupts
  199.  */
  200. void
  201. uninstall()
  202. {
  203.     Intblock *ibp;
  204.     IFPP vp;
  205.  
  206.     for (ibp = int_table; ibp->intnum >= 0; ibp++) {
  207.         if (ibp->oldint != NULL) {
  208.             vp = (IFPP) MK_FP(0, ibp->intnum*4);
  209.             disable();
  210.             *vp = ibp->oldint;
  211.             enable();
  212.         }
  213.     }
  214. }
  215.  
  216.  
  217. /* Read template file into buffer, setting pointers to
  218.  * beginning of lines.
  219.  * Sets template_text, templates and ntemplates.
  220.  * Pads out interrupt IDs to 6 characters.
  221.  * Returns number of lines.
  222.  */
  223. int
  224. read_template_file(char *filename)
  225. {
  226.     FILE *ifile;
  227.     char inline[ 100 ];
  228.     Intblock *ibp = int_table;
  229.     int i;
  230.  
  231.     if (! (ifile = fopen(filename, "rt")))
  232.         return 0;
  233.     while (fgets(inline, sizeof(inline), ifile)) {
  234.         if (ibp - int_table >= MAX_INTS)
  235.             return 0;
  236.         if (sscanf(inline, "%2x", &i) != 1)
  237.             continue;
  238.         if (ibp > int_table && i == (ibp-1)->intnum)
  239.             continue;    /* same as the previous one */
  240.         ibp->intnum = i;
  241.         ibp->oldint = NULL;
  242.         ibp++;
  243.     }
  244.     ibp->intnum = -1;        /* terminate the list */
  245.     fclose(ifile);
  246.     return 1;
  247. }
  248.  
  249.  
  250. /* output structures to intermediate file, call filter program
  251.  * return child return code (zero if OK)
  252.  */
  253. int
  254. output_file(char *tmpdir, char *outfilename, int longmode,
  255.     char *progname, char *tfilename)
  256. {
  257.     Swi_info huge *swip;
  258.     Swi_info outrec;
  259.     FILE *ofp;
  260.     char tempname[ 80 ];
  261.     int rval = 0;
  262.  
  263.     sprintf(tempname, "%s%c%s", tmpdir, sepchar, "intercXXXXXX");
  264.  
  265.     if (! mktemp(tempname)) {
  266.         fprintf(stderr, "%s: Bad temp file: %s\n", progname, tempname);
  267.         return -1;
  268.     }
  269.     if (! (ofp = fopen(tempname, "wb"))) {
  270.         fprintf(stderr, "%s: Can't open intermediate file %s\n",
  271.             progname, tempname);
  272.         return -1;
  273.     }
  274. #ifdef DEBUG
  275.     fprintf(stderr, "%s: Using \"%s\" as intermediate file\n",
  276.         progname, tempname);
  277. #endif
  278.     for (swip = swi_list; swip < (Swi_info huge *)swi_next; swip++) {
  279.         outrec = *swip;
  280.         if (fwrite(&outrec, sizeof(outrec), 1, ofp) != 1) {
  281.             fprintf(stderr, "%s: Write error on file %s\n", progname, tempname);
  282.             fclose(ofp);
  283.             unlink(tempname);
  284.             return -1;
  285.         }
  286.     }
  287.     fclose(ofp);
  288. #ifdef DEBUG
  289.     fprintf(stderr, "Running: " OFILTER " %s %s %s %s %s\n",
  290.         (longmode ? "-l" : " "), "-t", tfilename, tempname, outfilename
  291.         );
  292. #endif
  293.     rval = spawnlp(P_WAIT, OFILTER, OFILTER,
  294.         (longmode ? "-l" : " "), "-t", tfilename, tempname, outfilename
  295.         , NULL);
  296. #ifndef DEBUG
  297.     unlink(tempname);
  298. #endif
  299.     return rval;
  300. }
  301.  
  302. void
  303. main(int argc, char *argv[])
  304. {
  305.     static unsigned nswi = MAX_INTERRUPTS;    /* how many to record? */
  306.     static char *ofilename;    /* output file name */
  307.     static char *tfilename;    /* template file name */
  308.     static int childret;
  309.     static int longmode = 0;
  310.     static char tmpdir[ 64 ] = ".";
  311.     char *progname;
  312.  
  313.     /* spit out logo */
  314.     fprintf(stderr, "%s", logo);
  315.  
  316.     if ((switchar = getswitchar()) != '/')
  317.         sepchar = '/';
  318.     progname = strrchr(argv[0], sepchar) + 1;
  319.     *strchr(progname, '.') = '\0';
  320.  
  321.     /* process cmdline arguments */
  322.     if (argc < 2) {
  323.         fprintf(stderr, "Usage: %s %s", progname, usage);
  324.         exit(1);
  325.     }
  326.  
  327.     /* get TMP or TMPDIR (use ofilename as tmp var) */
  328.     if ((ofilename = getenv("TMPDIR")) || (ofilename = getenv("TMP")))
  329.         strncpy(tmpdir, ofilename, sizeof(tmpdir));
  330.  
  331.     ofilename = OFILENAME;
  332.     tfilename = TFILENAME;
  333.  
  334.     while (argv[1][0] == '-') {
  335.         char *dummy;    /* is this needed? */
  336.         switch (argv[1][1]) {
  337.             case 's':    /* specify max swi calls */
  338.             case 'S':
  339.                 nswi = (unsigned)strtol(argv[2], &dummy, 0);
  340.                 argv++;
  341.                 break;
  342.  
  343.             case 'o':    /* specify output filename */
  344.             case 'O':
  345.                 ofilename = argv[2];
  346.                 argv++;
  347.                 break;
  348.  
  349.             case 'd':    /* specify data filename */
  350.             case 'D':
  351.                 tfilename = argv[2];
  352.                 argv++;
  353.                 break;
  354.  
  355.             case 'l':    /* set long-mode output */
  356.             case 'L':
  357.                 longmode++;
  358.                 break;
  359.  
  360.             case 't':
  361.             case 'T':
  362.                 strncpy(tmpdir, argv[2], sizeof(tmpdir));
  363.                 argv++;
  364.                 break;
  365.  
  366.             default:
  367.                 fprintf(stderr, "%s: unknown option \"%s\"\n",progname,argv[1]);
  368.                 fprintf(stderr, "Usage: %s\t%s", progname, usage);
  369.                 exit(2);
  370.                 break;
  371.         }
  372.         argv++;
  373.     }
  374.  
  375.     if (! read_template_file( searchpath(tfilename) )) {
  376.         fprintf(stderr, "%s: error during read of \"%s\"\n",
  377.             progname, tfilename);
  378.         exit(5);
  379.     }
  380.  
  381.     /* obtain far segment for recording calls */
  382.     if (! get_swi_list(nswi)) {
  383.         fprintf(stderr, "%s: Can't get enough memory for %u swi records\n",
  384.             progname, nswi);
  385.         exit(3);
  386.     }
  387.     else
  388.         fprintf(stderr, "%s: Recording up to %u SWI records to file \"%s\"\n",
  389.             progname, nswi, ofilename);
  390.  
  391.     /* install our interrupt handler for each interrupt in the list */
  392.     install();
  393.  
  394.     /* now run the process */
  395.     childret = spawnvp(P_WAIT, argv[1], argv+1);
  396.  
  397.     /* and restore our captured interrupts */
  398.     uninstall();
  399.  
  400.     /* report on child return value */
  401.     if (childret == -1) {
  402.         fprintf(stderr, "%s: Spawn of \"%s\" failed: %s\n",
  403.             progname, argv[1], strerror(NULL));
  404.         exit(4);
  405.     }
  406.     if (childret != 0)
  407.         fprintf(stderr, "%s: Child process \"%s\" exit value: %d\n",
  408.             progname, argv[1], childret);
  409.  
  410.     /* output intermediate file and run output filter program */
  411.     if (output_file(tmpdir, ofilename, longmode, progname, tfilename))
  412.         fprintf(stderr, "%s: couldn't run output filter program " OFILTER "\n",
  413.             progname);
  414.  
  415.     farfree(swi_list);
  416.  
  417.     exit(0);
  418. }
  419.